package org.ns.framework.datasource.mybatis;

import java.util.HashSet;
import java.util.Set;

import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.TransactionIsolationLevel;
import org.ns.framework.datasource.database.SegmentMode;

/**
 * Mybatis 事务管理Builder
 * 
 * @author zhangxingyu
 *
 */
public class MybatisTransactionManageBuilder {

	private MybatisTransactionManageBuilder() {
	};

	/**
	 * 事务隔离管理构建.
	 */
	public static MybatisTransactionManageBuilder newBuilder() {
		return new MybatisTransactionManageBuilder();
	}

	private ExecutorType executorType; // 事务执行类型 SIMPLE, REUSE, BATCH

	private TransactionIsolationLevel level; // 事物级别

	private Set<SegmentMode> segmentModes; // 我的是否进行分割处理

	private MybatisTransactionManage parentReference; // 父引用.
	
	private Boolean autoCommit;
	
	private boolean inherit;

	public MybatisTransactionManageBuilder executorType(ExecutorType executorType) {
		this.executorType = executorType;
		return this;
	}

	public MybatisTransactionManageBuilder level(TransactionIsolationLevel level) {
		this.level = level;
		return this;
	}

	public MybatisTransactionManageBuilder segmentMode(Set<SegmentMode> segmentModes) {
		if (this.segmentModes == null) {
			this.segmentModes = segmentModes;
		}
		this.segmentModes.addAll(segmentModes);
		return this;
	}

	public MybatisTransactionManageBuilder segment(SegmentMode... segmentModes) {
		if (this.segmentModes == null) {
			this.segmentModes = new HashSet<SegmentMode>();
		}
		if (segmentModes == null || segmentModes.length == 0) {
			this.segmentModes.add(SegmentMode.segment);
		}
		for (SegmentMode segmentMode : segmentModes) {
			this.segmentModes.add(segmentMode);
		}
		return this;
	}

	/**
	 * 自动截断下面的子事物.
	 */
	public MybatisTransactionManageBuilder segmentChild() {
		return segment(SegmentMode.segmentChild);
	}

	public MybatisTransactionManageBuilder autoCommit(Boolean autoCommit) {
		this.autoCommit = autoCommit;
		return this;
	}
	
//	public MybatisTransactionManageBuilder autoCommit() {
//		return autoCommit(true);
//	}
	public MybatisTransactionManageBuilder inherit() {
		this.inherit = true;
		return this;
	}
	
	
	/**
	 * 是否隔离
	 * 
	 * @return
	 */
	private boolean isSegment() {
		return segmentModes != null && segmentModes.contains(SegmentMode.segment);
	}

	/**
	 * 事务分片--开始分片
	 */
	private void beforeSegment() {
		if (parentReference != null) {
			if (parentReference.existSegment(SegmentMode.tempSegment)) {// 存在单次的事务隔离
				segment(SegmentMode.segment); // 设置我将进行分割事务.
			}
		}
	}

	/**
	 * 事务分片--结束分片
	 */
	private void afterSegment() {
		if (parentReference != null) { // 发生需要切片事务, 结束事务隔离.
			parentReference.unsegment(SegmentMode.tempSegment); // 移除临时隔离.
		}
	}

	private void inherit(MybatisTransactionManage parentReference) {
		if (parentReference != null) {
			this.parentReference = parentReference;
			
			if (inherit) {
				// 继承并分割
				if (autoCommit == null) {
					autoCommit = parentReference.getAutoCommit();
				}
				if (segmentModes != null) { // 继承分割属性.
					segmentModes.addAll(parentReference.getChildSegmentMode());
				}
			}
			
			if (executorType == null) { // 事务执行模式
				executorType = parentReference.getExecutorType();
			}
			if (level == null) { // 事务级别
				level = parentReference.getLevel();
			}
			if (segmentModes == null) { // 继承分割属性.
				segmentModes = parentReference.getChildSegmentMode();
			}
			
			
		}
	}

	/**
	 * 创建或返回事务管理.
	 */
	public TransactionManage build() {
		return create();
	}

	/**
	 * 创建或返回事务管理.
	 */
	public TransactionManage create() {
		MybatisTransactionManage parentReference = MybatisTransactionChain.get();
		inherit(parentReference); // 继承操作
		// 如果父引用不存在或需要被分割进行分割
		if (parentReference == null || isSegment()) {
			beforeSegment(); // 开始
			MybatisTransactionManage impl = new MybatisTransactionManage();
			impl.setExecutorType(executorType);
			impl.setLevel(level);
			impl.setSegmentModes(segmentModes);
			impl.setAutoCommit(autoCommit); //  事务结束自动提交
			parentReference = impl;
			MybatisTransactionChain.set(impl);
			afterSegment(); // 结束
		}
		parentReference.reference(); // 获得事务进行计数 (对于隔离的事务, 计数首次==1, 复用事务则每次累加)
		return parentReference;
	}

}
